package com.googlecode.yoohoo.io.socket;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.common.IdleStatus;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.apache.mina.util.SessionLog;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.yoohoo.io.socket.stanza.Session;
import com.googlecode.yoohoo.io.socket.stream.NegotiationContext;
import com.googlecode.yoohoo.xmppcore.connection.IConnectionContext;
import com.googlecode.yoohoo.xmppcore.connection.IConnectionHandler;
import com.googlecode.yoohoo.xmppcore.stanza.ISession;

public class ConnectionAdaptor extends IoHandlerAdapter {
	private static final String KEY_SOCKET_XMPP_CONTEXT = "socket.xmpp.context";

	private Log log = LogFactory.getLog(ConnectionAdaptor.class);
	
	private IConnectionHandler connectionHandler;
	private BundleContext bundleContext;
	
	public ConnectionAdaptor(IConnectionHandler connectionHandler, BundleContext bundleContext) {
		if (connectionHandler == null) {
			if (log.isWarnEnabled()) {
				log.warn("Null connection handler. Socket connector maybe can't work.");
			}
		}
		this.connectionHandler = connectionHandler;
		this.bundleContext = bundleContext;
	}
	
	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		if (log.isDebugEnabled())
			log.debug("Caught [" + cause.getMessage() + "] from " + session);
		
		session.close();
	}
	
	private IConnectionContext getXmppContext(IoSession session) {
		IConnectionContext xmppContext = (IConnectionContext)session.getAttribute(KEY_SOCKET_XMPP_CONTEXT);
		if (xmppContext == null) {
			// stream negotiation starts
			xmppContext = new NegotiationContext(session, bundleContext);
			session.setAttribute(KEY_SOCKET_XMPP_CONTEXT, xmppContext);
			
			return xmppContext;
		}
		
		if (xmppContext instanceof ISession) {
			return xmppContext;
		}
		
		if (((NegotiationContext)xmppContext).isNegotiationDone()) {
			// stream negotiation is done. session starts.
			xmppContext = new Session(session, bundleContext);
			session.setAttribute(KEY_SOCKET_XMPP_CONTEXT, xmppContext);
		}
		
		return xmppContext;
	}

	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {		
		if (!(message instanceof String)) {
			session.close();
			log.warn("Received non-string message from " +  session);
			
			return;
		}

		if (connectionHandler != null) {
			connectionHandler.messageReceived(getXmppContext(session), (String)message);
		}
	}
	
	

	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		if (connectionHandler != null) {
			connectionHandler.messageSent(getXmppContext(session), (String)message);
		}
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
		if (connectionHandler == null)
			return;
		
		try {
			connectionHandler.sessionClosed(getXmppContext(session));
		} catch (Exception e) {
			throw e;
		} finally {
			session.removeAttribute(KEY_SOCKET_XMPP_CONTEXT);
		}
/*		if (session.containsAttribute(AppConstants.KEY_SESSION)) {
			ISession xmppSession = (ISession)session.getAttribute(AppConstants.KEY_SESSION);
			try {
				Presence lastPresence = xmppSession.getLastPresence();
				if (!PresenceType.UNAVAILABLE.toString().equals(lastPresence.getType()) &&
						!PresenceType.HIDDEN.toString().equals(lastPresence.getType())) {
					Presence unavailablePresence = new Presence();
					unavailablePresence.setType(PresenceType.UNAVAILABLE.toString());
					unavailablePresence.setFrom(xmppSession.getJabberId());
					PresenceHandler.deliverPresence(xmppSession, unavailablePresence);
				}
			} catch (Exception e) {
				throw e;
			} finally {
				((ServerContext)(xmppSession.getServerContext())).sessionRemoved(xmppSession);
				
				if (log.isDebugEnabled())
					log.debug(String.format("Session %s closed", session.toString()));
			}
		}*/
	}

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		super.sessionCreated(session);
/*		session.setIdleTime(IdleStatus.BOTH_IDLE, serverContext.getApplicationProperties(
				).getDefaultConnectionIdleTime());*/
	}

	@Override
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		if (connectionHandler != null) {
			connectionHandler.sessionIdle(getXmppContext(session));
		}

	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
		// SessionLog uses handler's logger and prints so mess debug infos.
		// so change it's logger to decompress mess infos.
		Logger log = LoggerFactory.getLogger(SessionLog.class);
		String prefix = (String) session.getAttribute(SessionLog.PREFIX);
		if (prefix == null) {
			prefix = "[" + session.getRemoteAddress() + "] ";
			session.setAttribute(SessionLog.PREFIX, prefix);
		}

		session.setAttribute(SessionLog.LOGGER, log);
		
		if (connectionHandler != null) {
			connectionHandler.sessionOpened(getXmppContext(session));
		}
	}
}
